/****************************************************************************
*                                                                           *
* Copyright (c) 2008 Nuvoton Technology Corp. All rights reserved.          *
*                                                                           *
*****************************************************************************/

/****************************************************************************
* FILENAME
*   pwm.c
*
* VERSION
*   1.0
*
* DESCRIPTION
*   The PWM device library of Nuvoton NUC900 Series MCU
*
* DATA STRUCTURES
*   typeTIMERVALUE  For pwmWrite usage
*   typePWMSTATUS   For pwmRead usage
*
* FUNCTIONS
*   pwmInit
*   pwmExit
*   pwmOpen
*   pwmClose
*   pwmRead
*   pwmWrite
*   pwmIoctl
*   pwmISR
*   pwmInitGPIO
*   pwmInitTimer
*   pwmStartTimer
*   pwmStopTimer
*   pwmSetCP
*   pwmSetDZI
*   pwmSetCSR
*   pwmSetDZGenerator
*   pwmSetTimerState
*   pwmSetInverter
*   pwmSetMode
*   pwmSetCNR
*   pwmSetCMR
*   pwmGetPDR
*   pwmSetPIER
*   pwmGetPIIR
*   pwmCleanPIIR
*
* HISTORY
*   2007-11-23    Ver 1.0 Create
*
* REMARK
*   Please notice that when user use pwmOpen to open a pwm timer, pwmOpen
*   will give the default setting of CSR, PPR, PCR, CMR, CNR to timer by
*   callng pwmInitTimer function. The default value is defined in [pwm.h].
*   User can use pwmIoctl function to change those setting after calling
*   pwmOpen function.
****************************************************************************/

#ifdef ECOS 
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "drv_api.h"
#include "diag.h"
#include "wbio.h"
#else
#include "wblib.h"
#endif

#include "NUC900_pwm.h"
#include "NUC900_reg.h"

//Internal funcfion definition
#ifdef ECOS
static cyg_uint32 pwmISR(cyg_vector_t vector, cyg_addrword_t data);
#else
VOID pwmISR(PVOID pvParam);
#endif

static INT pwmInitGPIO(VOID);
static INT pwmInitTimer(CONST INT nTimerIdentity);
static INT pwmStartTimer(CONST INT nTimerIdentity);
static INT pwmStopTimer(CONST INT nTimerIdentity, CONST INT nMethod);
// Register openation
static INT pwmSetCP(CONST INT nTimerIdentity, CONST INT nValue);
static INT pwmSetDZI(CONST INT nTimerIdentity, CONST INT nValue);
static INT pwmSetCSR(CONST INT nTimerIdentity, CONST INT nValue);
static INT pwmSetDZGenerator(CONST INT nTimerIdentity, CONST INT nStatus);
static INT pwmSetTimerState(CONST INT nTimerIdentity, CONST INT nStatus);
static INT pwmSetInverter(CONST INT nTimerIdentity, CONST INT nStatus);
static INT pwmSetMode(CONST INT nTimerIdentity, CONST INT nStatus);
static INT pwmSetCNR(CONST INT nTimerIdentity, CONST INT nValue);
static INT pwmSetCMR(CONST INT nTimerIdentity, CONST INT nValue);
static UINT pwmGetPDR(CONST INT nTimerIdentity);
static INT pwmSetPIER(CONST INT nTimerIdentity, CONST INT value);
//static INT pwmGetPIIR(CONST INT nTimerIdentity);
static INT pwmCleanPIIR(CONST INT nTimerIdentity);

//Global variable
static BOOL bPWMIRQFlag=FALSE;  //IRQ enable flag, set after PWM IRQ enable
static BOOL bPWMTimerOpenStatus[PWM_TIMER_NUM]; //timer flag which set after open(for disable IRQ decision)
static BOOL bPWMTimerStartStatus[PWM_TIMER_NUM]; //timer flag which set after Start count(to avoid incorrectly stop procedure)
static BOOL bPWMTimerMode[PWM_TIMER_NUM]; //PWM timer toggle/one shot mode
static BOOL volatile bPWMIntFlag[PWM_TIMER_NUM]; //interrupt flag which set by ISR

#ifdef ECOS
cyg_interrupt  pwm_int;
cyg_handle_t   pwm_int_handle;
#endif

/****************************************************************************
* FUNCTION
*   pwmInit
*
* DESCRIPTION
*   The init function of PWM device library
*
* CALLED BY
*   various components
*
* INPUTS
*   None
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT pwmInit(VOID)
{
    UINT temp;
    // Enable PWM clock
    temp = readw(REG_CLKEN);
	temp = temp | 0x00040000;
  	outpw(REG_CLKEN, temp);
	
#ifdef ECOS
    cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_PWM, 99, 0, pwmISR, NULL, &pwm_int_handle, &pwm_int);
    cyg_drv_interrupt_attach(pwm_int_handle);

#else	
    sysSetInterruptType(IRQ_PWM, HIGH_LEVEL_SENSITIVE);
    sysInstallISR(IRQ_LEVEL_1, IRQ_PWM, (PVOID)pwmISR);
    sysSetLocalInterrupt(ENABLE_IRQ);  // Enable CPSR I bit
#endif    
    return 0;
}

/****************************************************************************
* FUNCTION
*   pwmExit
*
* DESCRIPTION
*   The exit function of pwm device library
*
* CALLED BY
*   various components
*
* INPUTS
*   None
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     For future usage
****************************************************************************/
INT pwmExit(VOID)
{
    return 0;
}

/****************************************************************************
* FUNCTION
*   pwmOpen
*
* DESCRIPTION
*   The open function of PWM device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nTimerIdentity       PWM Timer channel identity
*
* OUTPUTS
*   Successful
*   pwmTimerBusy                PWM timer already open
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT pwmOpen(CONST INT nTimerIdentity)
{
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == TRUE)
    {
        return pwmTimerBusy;
    }
    if(bPWMIRQFlag == FALSE)
    {
#ifdef ECOS
		cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_PWM);
#else    
        sysEnableInterrupt(IRQ_PWM);
#endif        
        bPWMIRQFlag=TRUE;
    }
    bPWMTimerOpenStatus[nTimerIdentity]=TRUE;

    // Set PWM timer default value(CSR->PPR->PCR->CMR->CNR)
    pwmInitTimer(nTimerIdentity);

    //Enable PIER
    pwmSetPIER(nTimerIdentity, PWM_ENABLE);

    //Reset PIIR
    pwmCleanPIIR(nTimerIdentity);

    //Reset PWM timer start count flag
    bPWMTimerStartStatus[nTimerIdentity] = FALSE;
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmClose
*
* DESCRIPTION
*   The close function of PWM device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nTimerIdentity       PWM Timer channel identity
*
* OUTPUTS
*   Scccessful
*   pwmTimerNotOpen             PWM timer not open
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT pwmClose(CONST INT nTimerIdentity)
{
    INT nLoop;
    BOOL uAllTimerClose=TRUE;
    if( nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX )
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE)
    {
        return pwmTimerNotOpen;
    }
    bPWMTimerOpenStatus[nTimerIdentity] = FALSE;
    //Check if all timer stop, IRQ can be disable
    for(nLoop=PWM_TIMER_MIN;nLoop<PWM_TIMER_NUM;nLoop++)
    {
        if(bPWMTimerOpenStatus[nLoop] == TRUE)
        {
            uAllTimerClose=FALSE;
        }
    }
    //All timer stop, disable IRQs
    if(uAllTimerClose == TRUE)
    {
#ifdef ECOS
        cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_PWM);
#else    
        sysDisableInterrupt(IRQ_PWM);
#endif
        bPWMIRQFlag=FALSE;
    }

    pwmSetPIER(nTimerIdentity, PWM_DISABLE);
    pwmCleanPIIR(nTimerIdentity);

#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmRead
*
* DESCRIPTION
*   The read function of PWM device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nTimerIdentity      PWM Timer channel identity
*   pucStatusValue      The point of typePWMSTATUS
*   uLength             The length of typePWMSTATUS
*
* OUTPUTS
*   Successful
*   pwmTimerNotOpen             PWM timer not open
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidStructLength      Struct length error(struct type error)
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT pwmRead(CONST INT nTimerIdentity, PUCHAR pucStatusValue, CONST UINT uLength)
{
    if( nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX )
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE)
    {
        return pwmTimerNotOpen;
    }
    if(uLength != sizeof(typePWMSTATUS))
    {
        return pwmInvalidStructLength;// Struct length error(struct type error)
    }
    if( sizeof(*((typePWMSTATUS *)pucStatusValue)) != sizeof(typePWMSTATUS) )
    {
        return pwmInvalidStructLength;// Struct length error(struct type error)
    }
    ((typePWMSTATUS *)pucStatusValue)->PDR=pwmGetPDR(nTimerIdentity);
    if(bPWMIntFlag[nTimerIdentity] == TRUE)
    {
        bPWMIntFlag[nTimerIdentity]=FALSE;
        ((typePWMSTATUS *)pucStatusValue)->InterruptFlag=TRUE;
    }
    else
    {
        ((typePWMSTATUS *)pucStatusValue)->InterruptFlag=FALSE;
    }
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmWrite
*
* DESCRIPTION
*   The write function of PWM device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nTimerIdentity      PWM Timer channel identity
*   pucCNRCMRValue      The value of CNR and CMR
*   uLength             For future usage
*
* OUTPUTS
*   Successful
*   pwmTimerNotOpen             PWM timer not open
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT pwmWrite(CONST INT nTimerIdentity, PUCHAR pucCNRCMRValue, CONST UINT uLength)
{
    typeTIMERVALUE timervalue;
    INT nStatus;
    if( nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX )
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE)
    {
        return pwmTimerNotOpen;
    }
    if(uLength != sizeof(typeTIMERVALUE))
    {
        return pwmInvalidStructLength;// Struct length error(struct type error)
    }
    timervalue.value=((typeTIMERVALUE *)pucCNRCMRValue)->value;
    nStatus=pwmSetCNR(nTimerIdentity, timervalue.field.cnr);
#ifdef ECOS     
    if(nStatus != 0)
#else
    if(nStatus != Successful)
#endif    
    {
        return nStatus;
    }
    nStatus=pwmSetCMR(nTimerIdentity, timervalue.field.cmr);
#ifdef ECOS     
    if(nStatus != 0)
#else
    if(nStatus != Successful)
#endif 
    {
        return nStatus;
    }
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmIoctl
*
* DESCRIPTION
*   The ioctl function of PWM device library
*
* CALLED BY
*   various components
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   uCommand            Ioctl command which indicates different operation
*   uIndication         not use in PWM
*   uValue              The value which use with uCommand
*
* OUTPUTS
*   Successful
*   pwmTimerNotOpen             PWM timer not open
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidIoctlCommand      Ioctl command error
*   Others                      according to different uCommand
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
INT pwmIoctl(CONST INT nTimerIdentity, CONST UINT uCommand, CONST UINT uIndication, UINT uValue)
{
    INT nStatus;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE)
    {
        return pwmTimerNotOpen;
    }
    switch(uCommand)
    {
        case START_PWMTIMER:
        {
            nStatus=pwmStartTimer(nTimerIdentity);
            break;
        }
        case STOP_PWMTIMER:
        {
            // default stop method is 2
            nStatus=pwmStopTimer(nTimerIdentity, PWM_STOP_METHOD2);
            break;
        }
        case SET_CSR:
        {
            nStatus=pwmSetCSR(nTimerIdentity, uValue);
            break;
        }
        case SET_CP:
        {
            nStatus=pwmSetCP(nTimerIdentity, uValue);
            break;
        }
        case SET_DZI:
        {
            nStatus=pwmSetDZI(nTimerIdentity, uValue);
            break;
        }
        case SET_INVERTER:
        {
            nStatus=pwmSetInverter(nTimerIdentity, uValue);
            break;
        }
        case SET_MODE:
        {
            nStatus=pwmSetMode(nTimerIdentity, uValue);
            break;
        }
        case ENABLE_DZ_GENERATOR:
        {
            nStatus=pwmSetDZGenerator(nTimerIdentity, PWM_ENABLE);
            break;
        }
        case DISABLE_DZ_GENERATOR:
        {
            nStatus=pwmSetDZGenerator(nTimerIdentity, PWM_DISABLE);
            break;
        }
        case ENABLE_PWMGPIOOUTPUT:
        {
            nStatus=pwmInitGPIO();
            break;
        }
        default:
        {
            return pwmInvalidIoctlCommand;
            break;
        }
    }
    return nStatus;
}

/****************************************************************************
* FUNCTION
*   pwmISR
*
* DESCRIPTION
*   The interrupt service routines of PWM
*
* CALLED BY
*   sysIrqHandler
*
* INPUTS
*   pvParam     IRQ Parameter(not use in PWM)
*
* OUTPUTS
*   None
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu  12/27/05    Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
#ifdef ECOS
static cyg_uint32 pwmISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID pwmISR(PVOID pvParam)
#endif
{
    INT nLoop;

    UINT32 uRegisterValue = 0;
    uRegisterValue = inpw(REG_PWM_PIIR);// Get PIIR value
    for(nLoop=PWM_TIMER_MIN;nLoop<PWM_TIMER_NUM;nLoop++)
    {
        if(uRegisterValue&(1<<nLoop))
        {
            bPWMIntFlag[nLoop] = 1;
            outpw(REG_PWM_PIIR,(0xF)&(~(1<<nLoop)));
        }
    }

#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif	
}

/****************************************************************************
* FUNCTION
*   pwmInitGPIO
*
* DESCRIPTION
*   This function set corresponding GPIO as PWM function according to the
*   parameter nTimerIdentity
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmInitGPIO(VOID)
{
    UINT temp=0;
   	temp = readw(REG_MFSEL) & 0xFF3FFFFF;;
  	temp = temp | 0x00400000;
  	outpw(REG_MFSEL,temp);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmInitTimer
*
* DESCRIPTION
*   This function initiates PWM timer n and set the default setting to CSR,
*   PPR, PCR, CNR, CMR
*
* CALLED BY
*   pwmOpen
*
* INPUTS
*   nTimerIdentity      Timer channel number
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   Other                       according to different functions
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmInitTimer(CONST INT nTimerIdentity)
{
    typePPR PWMPPR;
    INT nStatus;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }

    //Set CSR
    nStatus=pwmSetCSR(nTimerIdentity, DEFAULT_CSR);
#ifdef ECOS     
    if(nStatus != 0)
#else
    if(nStatus != Successful)
#endif 
    {
        return nStatus;
    }

    //Set PPR
    PWMPPR.value=(UINT)inpw(REG_PWM_PPR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            if(PWMPPR.field.cp0 == 0)
            {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
        case PWM_TIMER1:
        {
            if(PWMPPR.field.cp0 == 0)
            {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
        case PWM_TIMER2:
        {
            if(PWMPPR.field.cp1 == 0)
            {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
        case PWM_TIMER3:
        {
            if(PWMPPR.field.cp1 == 0)
            {
                pwmSetCP(nTimerIdentity, DEFAULT_CP);
            }
            break;
        }
    }

    //Set PCR
    nStatus=pwmSetMode(nTimerIdentity, DEFAULT_MODE);
#ifdef ECOS     
    if(nStatus != 0)
#else
    if(nStatus != Successful)
#endif 
    {
        return nStatus;
    }
    bPWMTimerMode[nTimerIdentity] = DEFAULT_MODE;

    //Set CMR
    nStatus=pwmSetCMR(nTimerIdentity, DEFAULT_CMR);
#ifdef ECOS     
    if(nStatus != 0)
#else
    if(nStatus != Successful)
#endif 
    {
        return nStatus;
    }

    //Set CNR
    nStatus=pwmSetCNR(nTimerIdentity, DEFAULT_CNR);
#ifdef ECOS     
    if(nStatus != 0)
#else
    if(nStatus != Successful)
#endif 
    {
        return nStatus;
    }

#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmStartTimer
*
* DESCRIPTION
*   This function starts PWM timer n according to the
*   parameter nTimerIdentity
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmStartTimer(CONST INT nTimerIdentity)
{
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    pwmSetTimerState(nTimerIdentity, PWM_ENABLE);
    if(bPWMTimerMode[nTimerIdentity] == PWM_TOGGLE)
    {
        bPWMTimerStartStatus[nTimerIdentity] = TRUE;
    }
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmStopTimer
*
* DESCRIPTION
*   This function stops PWM timer n using method 1, 2, or 3 according to the
*   parameter nTimerIdentity and nStatus
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity  Timer channel number
*   nMethod         Stop PWM timer method
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidStopMethod        Stop method error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmStopTimer(CONST INT nTimerIdentity, INT nMethod)
{
    typeCNR PWMCNR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        // Timer_num value error
        return pwmInvalidTimerChannel;
    }
    //Can't stop before open PWM timer
    if(bPWMTimerOpenStatus[nTimerIdentity] == FALSE)
    {
#ifdef ECOS
	    return 0;
#else
        return Successful;
#endif
    }
    // one shot mode didn't need stop procedure
    if(bPWMTimerMode[nTimerIdentity] == PWM_ONESHOT)
    {
#ifdef ECOS
	    return 0;
#else
        return Successful;
#endif
    }
    // Timer stop already, no need to stop again
    if(bPWMTimerStartStatus[nTimerIdentity] == FALSE)
    {
#ifdef ECOS
	    return 0;
#else
        return Successful;
#endif
    }

	// Set CNR as 0
	PWMCNR.field.cnr=0;
    outpw(REG_PWM_CNR0+(PWM_OFFSET*nTimerIdentity), PWMCNR.value);

	switch(nMethod)
	{
	    case PWM_STOP_METHOD1:
	    {
		    while(1)
		    {
			    if(pwmGetPDR(nTimerIdentity) == 0)// Wait PDR reach to 0
			    {
				    pwmSetTimerState(nTimerIdentity, PWM_DISABLE);// Disable pwm timer
				    bPWMIntFlag[nTimerIdentity]=FALSE;
				    bPWMTimerStartStatus[nTimerIdentity] = FALSE;
				    break;
			    }
		    }
	        break;
	    }
	    case PWM_STOP_METHOD2:
	    {
		    while(1)
		    {
			    if(bPWMIntFlag[nTimerIdentity] == TRUE)// Wait interrupt happen
			    {
				    pwmSetTimerState(nTimerIdentity, PWM_DISABLE);// Disable pwm timer
				    bPWMIntFlag[nTimerIdentity] = FALSE;
				    bPWMTimerStartStatus[nTimerIdentity] = FALSE;
				    break;
			    }
		    }
	        break;
	    }
	    /*case PWM_STOP_METHOD3:
	    {
	        pwmSetPCRState(nTimerIdentity, PWM_DISABLE);// Disable pwm timer
	        bPWMIntFlag[nTimerIdentity] = FALSE;
	        bPWMTimerStartStatus[nTimerIdentity] = FALSE;
	        break;
	    }*/
	    default:
	    {
	        return pwmInvalidStopMethod;// Stop method value error
	        break;
	    }
	}
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetCP
*
* DESCRIPTION
*   This function set CPn value according to the
*   parameter nTimerIdentity and nValue
*
* CALLED BY
*   pwmIoctl
*   pwmInitTimer
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nValue              The value which want to set in CSRn
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*	pwmInvalidCPValue			PWM_PPR CPn value out of range
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetCP(CONST INT nTimerIdentity, CONST INT nValue)
{
    typePPR PWMPPR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue<CP_MIN || nValue>CP_MAX)
    {
       return pwmInvalidCPValue;// CP value error
    }
    PWMPPR.value=(UINT)inpw(REG_PWM_PPR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMPPR.field.cp0=nValue;
            break;
        }
        case PWM_TIMER1:
        {
            PWMPPR.field.cp0=nValue;
            break;
        }
        case PWM_TIMER2:
        {
            PWMPPR.field.cp1=nValue;
            break;
        }
        case PWM_TIMER3:
        {
            PWMPPR.field.cp1=nValue;
            break;
        }
    }
    outpw(REG_PWM_PPR, PWMPPR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetDZI
*
* DESCRIPTION
*   This function set DZIn value according to the
*   parameter nTimerIdentity and nValue
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nValue              The value which want to set in CSRn
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*	pwmInvalidDZIValue			PWM_PPR DZIn out of range
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetDZI(CONST INT nTimerIdentity, CONST INT nValue)
{
    typePPR PWMPPR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue<DZI_MIN || nValue>DZI_MAX)
    {
       return pwmInvalidDZIValue;// CSR value error
    }
    PWMPPR.value=(UINT)inpw(REG_PWM_PPR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMPPR.field.dzi0=nValue;
            break;
        }
        case PWM_TIMER1:
        {
            PWMPPR.field.dzi0=nValue;
            break;
        }
        case PWM_TIMER2:
        {
            PWMPPR.field.dzi1=nValue;
            break;
        }
        case PWM_TIMER3:
        {
            PWMPPR.field.dzi1=nValue;
            break;
        }
    }
    outpw(REG_PWM_PPR, PWMPPR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetCSR
*
* DESCRIPTION
*   This function set CSRn value according to the
*   parameter nTimerIdentity and nValue
*
* CALLED BY
*   pwmIoctl
*   pwmInitTimer
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nValue              The value which want to set in CSRn
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetCSR(CONST INT nTimerIdentity, CONST INT nValue)
{
    typeCSR PWMCSR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue<CSR_MIN || nValue>CSR_MAX)
    {
       return pwmInvalidCSRValue;// CSR value error
    }
    PWMCSR.value=(UINT)inpw(REG_PWM_CSR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMCSR.field.csr0=nValue;
            break;
        }
        case PWM_TIMER1:
        {
            PWMCSR.field.csr1=nValue;
            break;
        }
        case PWM_TIMER2:
        {
            PWMCSR.field.csr2=nValue;
            break;
        }
        case PWM_TIMER3:
        {
            PWMCSR.field.csr3=nValue;
            break;
        }
    }
    outpw(REG_PWM_CSR, PWMCSR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetDZGenerator
*
* DESCRIPTION
*   This function set PCR channel n toggle/one shot mode according to the
*   parameter nTimerIdentity and nStatus
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nStatus             PWM_TOGGLE/PWM_ONESHOT
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidDZGStatus         PWM Dead-Zone Generator enable/disable status error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetDZGenerator(CONST INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWMDZG_ENABLE && nStatus != PWMDZG_DISABLE)
    {
        return pwmInvalidDZGStatus;// PCR inverter value error
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMPCR.field.grpup0_dzen=nStatus;
            break;
        }
        case PWM_TIMER1:
        {
            PWMPCR.field.grpup0_dzen=nStatus;
            break;
        }
        case PWM_TIMER2:
        {
            PWMPCR.field.grpup1_dzen=nStatus;
            break;
        }
        case PWM_TIMER3:
        {
            PWMPCR.field.grpup1_dzen=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetPCRState
*
* DESCRIPTION
*   This function set PCR channel n enable/disable according to the
*   parameter nTimerIdentity and nStatus
*
* CALLED BY
*   pwmStartTimer
*   pwmStopTimer
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nStatus             PWM_ENABLE/PWMDISABLE
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetTimerState(CONST INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWM_ENABLE && nStatus != PWM_DISABLE)
    {
        return pwmInvalidTimerStatus;
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMPCR.field.ch0_en=nStatus;
            break;
        }
        case PWM_TIMER1:
        {
            PWMPCR.field.ch1_en=nStatus;
            break;
        }
        case PWM_TIMER2:
        {
            PWMPCR.field.ch2_en=nStatus;
            break;
        }
        case PWM_TIMER3:
        {
            PWMPCR.field.ch3_en=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetInverter
*
* DESCRIPTION
*   This function set PCR channel n inverter on/off according to the
*   parameter nTimerIdentity and nStatus
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nStatus             PWM_ENABLE/PWMDISABLE
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidInverterValue     PCR inverter value error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetInverter(CONST INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWM_INVON && nStatus != PWM_INVOFF)
    {
        return pwmInvalidInverterValue;// PCR inverter value error
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMPCR.field.ch0_inverter=nStatus;
            break;
        }
        case PWM_TIMER1:
        {
            PWMPCR.field.ch1_inverter=nStatus;
            break;
        }
        case PWM_TIMER2:
        {
            PWMPCR.field.ch2_inverter=nStatus;
            break;
        }
        case PWM_TIMER3:
        {
            PWMPCR.field.ch3_inverter=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetMode
*
* DESCRIPTION
*   This function set PCR channel n toggle/one shot mode according to the
*   parameter nTimerIdentity and nStatus
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nStatus             PWM_TOGGLE/PWM_ONESHOT
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*	pwmInvalidModeStatus		PWM_PCR mode value error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetMode(CONST INT nTimerIdentity, INT nStatus)
{
    typePCR PWMPCR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nStatus != PWM_TOGGLE && nStatus != PWM_ONESHOT)
    {
        return pwmInvalidModeStatus;// PCR inverter value error
    }
    PWMPCR.value=(UINT)inpw(REG_PWM_PCR);
    switch(nTimerIdentity)
    {
        case PWM_TIMER0:
        {
            PWMPCR.field.ch0_mode=nStatus;
            break;
        }
        case PWM_TIMER1:
        {
            PWMPCR.field.ch1_mode=nStatus;
            break;
        }
        case PWM_TIMER2:
        {
            PWMPCR.field.ch2_mode=nStatus;
            break;
        }
        case PWM_TIMER3:
        {
            PWMPCR.field.ch3_mode=nStatus;
            break;
        }
    }
    outpw(REG_PWM_PCR, PWMPCR.value);
    bPWMTimerMode[nTimerIdentity] = nStatus;
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetCNR
*
* DESCRIPTION
*   This function set PWM_CNRn value according to the
*   parameter nTimerIdentity and nValue
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nValue
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidCNRValue
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetCNR(CONST INT nTimerIdentity, INT nValue)
{
    typeCNR PWMCNR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue < CNR_MIN || nValue > CNR_MAX)
    {
        return pwmInvalidCNRValue;// PCR inverter value error
    }
    PWMCNR.field.cnr=nValue;
    outpw(REG_PWM_CNR0+(PWM_OFFSET*nTimerIdentity), PWMCNR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmSetCMR
*
* DESCRIPTION
*   This function set PWM_CMRn value according to the
*   parameter nTimerIdentity and nValue
*
* CALLED BY
*   pwmIoctl
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nValue
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*   pwmInvalidCMRValue          PWM_CMR value out of range
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetCMR(CONST INT nTimerIdentity, INT nValue)
{
    typeCMR PWMCMR;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    if(nValue < CMR_MIN || nValue > CMR_MAX)
    {
        return pwmInvalidCMRValue;// CMR value error
    }
    PWMCMR.field.cmr=nValue;
    outpw(REG_PWM_CMR0+(PWM_OFFSET*nTimerIdentity), PWMCMR.value);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}

/****************************************************************************
* FUNCTION
*   pwmGetPDR
*
* DESCRIPTION
*   This function return the PDR value of PWM timer n
*
* CALLED BY
*   pwmStopTimer
*   pwmRead
*
* INPUTS
*   nTimerIdentity      Timer channel number
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static UINT pwmGetPDR(CONST INT nTimerIdentity)
{
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    else
    {
        return (UINT)inpw(REG_PWM_PDR0+(PWM_OFFSET*nTimerIdentity));// Return PDR value
    }
}

/****************************************************************************
* FUNCTION
*   pwmSetPIER
*
* DESCRIPTION
*   This function set the PIERn bit of PWM timer n as 1 or 0 according to the
*   parameter nTimerIdentity and nValue
*
* CALLED BY
*   pwmOpen
*   pwmclose
*
* INPUTS
*   nTimerIdentity      Timer channel number
*   nValue              PWM_ENABLE/PWM_DISABLE
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmSetPIER(CONST INT nTimerIdentity, INT nValue)
{
    UINT uRegisterValue=0;;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// Timer_num value error
    }
    else
    {
        uRegisterValue=(UINT)inpw(REG_PWM_PIER);
        if(nValue==PWM_ENABLE)
        {
            uRegisterValue=uRegisterValue|(1<<nTimerIdentity);// Set PIER
        }
        else
        {
            uRegisterValue=uRegisterValue&(0<<nTimerIdentity);// Clear PIER
        }
        outpw(REG_PWM_PIER, uRegisterValue);// Write value to PIER
#ifdef ECOS
	    return 0;
#else
        return Successful;
#endif
    }
}

/****************************************************************************
* FUNCTION
*   pwmGetPIIR
*
* DESCRIPTION
*   This function return the PIIRn bit value of PWM timer n according to the
*   parameter nTimerIdentity
*
* CALLED BY
*   pwmISR
*
* INPUTS
*   nTimerIdentity      Timer channel number
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
/*
static INT pwmGetPIIR(CONST INT nTimerIdentity)
{
    UINT uRegisterValue=0;
    uRegisterValue=(UINT)inpw(REG_PWM_PIIR);// Get PIIR value
    uRegisterValue=(uRegisterValue&(1<<nTimerIdentity))>>nTimerIdentity;// Get PIIRn value of timer n
    return(uRegisterValue);// Return PIIRn value of timer n
}
*/

/****************************************************************************
* FUNCTION
*   pwmCleanPIIR
*
* DESCRIPTION
*   This function clean PIIRn bit according to the
*   parameter nTimerIdentity
*
* CALLED BY
*   pwmOpen
*   pwmISR
*
* INPUTS
*   nTimerIdentity      Timer channel number
*
* OUTPUTS
*   Successful
*   pwmInvalidTimerChannel      PWM Timer channel number error
*
* HISTORY
*   NAME        DATE        REMARKS
*   NM51 WTLiu	12/27/05	Created initial version 1.0
*
* REMARK
*     None
****************************************************************************/
static INT pwmCleanPIIR(CONST INT nTimerIdentity)
{
    UINT uRegisterValue=0;
    if(nTimerIdentity<PWM_TIMER_MIN || nTimerIdentity>PWM_TIMER_MAX)
    {
        return pwmInvalidTimerChannel;// nTimerIdentity value error
    }
    uRegisterValue=(UINT)inpw(REG_PWM_PIIR);
    uRegisterValue=uRegisterValue&~(1<<nTimerIdentity);
    outpw(REG_PWM_PIIR,uRegisterValue);
#ifdef ECOS
	return 0;
#else
    return Successful;
#endif
}
